package com.manzhushaka.spring;

import com.manzhushaka.demo.config.AppConfig;
import com.manzhushaka.spring.annotation.Autowired;
import com.manzhushaka.spring.annotation.Component;
import com.manzhushaka.spring.annotation.ComponentScan;
import com.manzhushaka.spring.annotation.Scope;
import com.manzhushaka.spring.core.BeanDefinition;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContext {

    private AppConfig appConfig;

    //单例bean对象池
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    //存放beanDefinition的map
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //有参构造方法
    public ApplicationContext(AppConfig appConfig) {

        this.appConfig = appConfig;

        //开始扫描包
        //先拿到appConfig上面的注解,获得扫描包的位置
        ComponentScan componentScan = this.appConfig.getClass().getAnnotation(ComponentScan.class);

        //获得扫描包路径，并转换成/的格式
        String packageName = componentScan.value().replace(".", "/");

        //拿到类加载器
        ClassLoader classLoader = ApplicationContext.class.getClassLoader();

        //拿到编译后的bean文件路径
        URL url = classLoader.getResource(packageName);

        //转换成文件形式
        File file = new File(url.getFile());

        //如果这个路径是文件夹，就拿到包下面的所有.class文件的绝对路径 todo 目前只能拿到一层，也就是说如果bean文件夹下面还有文件夹，就没办法了
        if (file.isDirectory()){

            File[] files = file.listFiles();
            for (File f : files) {

                //获得绝对路径
                String absolutePath = f.getAbsolutePath();
                //判断文件的后缀名是否是.class,也就是编译后的文件
                if(absolutePath.endsWith(".class")){

                    //获得类的全限定名
                    String className = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));
                    className = className.replace("\\", ".");

                    //通过类加载器和全限定名获得Class对象
                    try{
                        Class<?> clazz = classLoader.loadClass(className);

                        //看看这个类上面有没有spring的Component注解
                        if(clazz.isAnnotationPresent(Component.class)){

                            //获得注解上的值作为beanName
                            Component annotation = clazz.getAnnotation(Component.class);
                            String beanName = annotation.value();

                            //如果注解的value为空，就使用类名作为beanName，并按照类名首字母小写的方式作为beanName（spring的标准）
                            if (beanName.equals("")){
                                beanName = Introspector.decapitalize(clazz.getSimpleName());
                            }

                            System.out.println("找到一个bean：" + beanName);

                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setType(clazz);

                            if (clazz.isAnnotationPresent(Scope.class)){

                                beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());

                            }else {

                                beanDefinition.setScope("singleton");

                            }

                            beanDefinitionMap.put(beanName, beanDefinition);
                            System.out.println(beanName + "已经注入容器");

                        }

                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }

                }
            }

        }

    }

    public Object getBean(String beanName){

        //先通过beanName找到beanDefinition
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition == null){

            throw new NullPointerException("beanName不存在");

        }

        String scope = beanDefinition.getScope();

        //如果是单例bean，就要从单例池中找，如果找不到就createBean一个塞进去并返回,如果不是，则直接createBean并返回
        if (scope.equals("singleton")){

            if (singletonObjects.containsKey(beanName)){

                return singletonObjects.get(beanName);

            }else {

                Object bean = createBean(beanDefinition);
                singletonObjects.put(beanName, bean);
                return bean;

            }
        }else {

            return createBean(beanDefinition);

        }

    }

    /**
     * 创建bean
     * @param beanDefinition
     * @return
     */
    private Object createBean(BeanDefinition beanDefinition){

        //获得Class对象
        Class clazz = beanDefinition.getType();

        try {

            //通过反射获得无参构造方法，并且创建对象
            Object instance = clazz.getConstructor().newInstance();

            //依赖注入
            for (Field field : clazz.getDeclaredFields()) {

                if (field.isAnnotationPresent(Autowired.class)){

                    //通过反射注入字段，需要设置一下这个属性，不然没法set
                    field.setAccessible(true);

                    field.set(instance, getBean(field.getName()));

                    field.setAccessible(false);

                }
            }

            return instance;

        } catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {

            throw new RuntimeException(e);

        }

    }

}
